﻿using System.Linq;
using Sustainalytics.Reporting.Framework;
using Foxit.PDF.Merger;
using System.Collections.Generic;
using Foxit.PDF;
using Foxit.PDF.PageElements;
using Foxit.PDF.Imaging;
using Sustainalytics.Utils;

namespace Sustainalytics.Governance.Reporting
{
    public class CompanyReportGenerator : ICompanyReportGenerator
    {
        # region Static Images

        private static byte[] _companyLogoFooterImage;
        public byte[] CompanyLogoFooterImage { get { return _companyLogoFooterImage; } }

        private static void LoadStaticImages()
        {
            if (_companyLogoFooterImage == null)
                _companyLogoFooterImage = ImageManager.GetJpgImage(@"Images\sustaynalytics_logo_footer.jpg");
        }

        #endregion

        static CompanyReportGenerator()
        {
            LoadStaticImages();
        }


        public GeneratedPdfReport GeneratePdfReport(ReportDataSource ds, bool onlyMerge = false)
        {
            var report = new PdfReport();
            report.Document.Title = "Corporate Governance Company Report";

            PdfReportSection companyOverviewSection = null;
            PdfReportSection issuesSection = null;
            PdfReportSection indicatorsSection = null;
            PdfReportSection sourcesSection = null;
            PdfReportSection finalSection = null;

            PdfDocument pdfDocument;

            new System.Action[]{
                () => { companyOverviewSection = GetCompanyOverviewSection(ds); },
                () => { issuesSection = GetIssuesSection(ds); },
                () => { indicatorsSection = GetIndicatorsSection(ds); },
                () => { sourcesSection = GetSourcesSection(ds); },
                () => { finalSection = GetFinalSection(ds); }
            }.RunInParallel();
            

            var result = new GeneratedPdfReport
            {
                Id = ds.CompanyId,
                Name = ds.CompanyProfile.CompanyName,
                PdfDocument = new byte[0],
                Sections = new List<PdfReportSection>()
            };

            if (companyOverviewSection != null && companyOverviewSection.PdfDocument.Length > 0)
            {
                pdfDocument = new PdfDocument(companyOverviewSection.PdfDocument);

                for (var page = 1; page <= pdfDocument.Pages.Count; page++)
                {
                    report.AddPage(new ImportedPage(pdfDocument.GetPage(page)));
                }

                result.Sections.Add(companyOverviewSection);

            }

            var issuesPageOffset = 0;

            if (issuesSection != null && issuesSection.PdfDocument.Length > 0)
            {
                issuesPageOffset = report.PageCounter;

                pdfDocument = new PdfDocument(issuesSection.PdfDocument);

                for (var number = 1; number <= pdfDocument.Pages.Count; number++)
                {
                    var page = pdfDocument.GetPage(number);

                    report.AddPage(new ImportedPage(page));
                }

                result.Sections.Add(issuesSection);
            }

            if (ds.BusinessAndFinancialProfilePdf != null)
            {
                pdfDocument = new PdfDocument(ds.BusinessAndFinancialProfilePdf);

                for (var number = 1; number <= pdfDocument.Pages.Count; number++)
                {
                    var page = pdfDocument.GetPage(number);
                    report.AddPage(new ImportedPage(page));
                }
            }

            if (ds.GovernanceDataProfilePdf != null)
            {
                pdfDocument = new PdfDocument(ds.GovernanceDataProfilePdf);

                for (var page = 1; page <= pdfDocument.Pages.Count; page++)
                {
                    report.AddPage(new ImportedPage(pdfDocument.GetPage(page)));
                }
            }

            var indicatorsPageOffset = 0;

            if (indicatorsSection != null && indicatorsSection.PdfDocument.Length > 0)
            {
                indicatorsPageOffset = report.PageCounter;

                pdfDocument = new PdfDocument(indicatorsSection.PdfDocument);

                for (var page = 1; page <= pdfDocument.Pages.Count; page++)
                {
                    report.AddPage(new ImportedPage(pdfDocument.GetPage(page)));
                }

                result.Sections.Add(indicatorsSection);
            }

            if (sourcesSection != null && sourcesSection.PdfDocument.Length > 0)
            {
                pdfDocument = new PdfDocument(sourcesSection.PdfDocument);

                for (var page = 1; page <= pdfDocument.Pages.Count; page++)
                {
                    report.AddPage(new ImportedPage(pdfDocument.GetPage(page)));
                }

                result.Sections.Add(sourcesSection);
            }

            if (finalSection != null && finalSection.PdfDocument.Length > 0)
            {
                pdfDocument = new PdfDocument(finalSection.PdfDocument);

                for (var page = 1; page <= pdfDocument.Pages.Count; page++)
                {
                    report.AddPage(new ImportedPage(pdfDocument.GetPage(page)));
                }

                result.Sections.Add(finalSection);
            }

            if (issuesSection != null && issuesSection.PageElementPositions.Any())
            {
                const float horizontalPositionOffset = 0.0f;
                const float verticalPositionOffset = 0.0f;                
                
                LinkIssuesElements(report, issuesPageOffset, indicatorsPageOffset,
                    horizontalPositionOffset, verticalPositionOffset,
                    issuesSection.PageElementPositions, indicatorsSection.OutlinePositions);
            }

            AddPageFooter(report);

            if (report.PageCounter > 0)
            {
                result.PdfDocument = report.Draw();
            }

            return result;
        }

        private void LinkIssuesElements(PdfReport report, int issuesPageOffset, int indicatorsPageOffset,
            float horizontalPositionOffset, float verticalPositionOffset,
            List<PageElementPosition> pageElementPositions, List<OutlinePosition> outlinePositions)
        {
            if (!pageElementPositions.Any()) return;
            
            var parentOutlinePosition = outlinePositions[0];

            foreach (var pageElementPosition in pageElementPositions)
            {
                var indicatorOutline = parentOutlinePosition.ChildOutlinePositions
                    .SingleOrDefault(op => op.Id == pageElementPosition.Id);

                if (indicatorOutline == null) continue;

                var pageNumber = pageElementPosition.PageNumber + issuesPageOffset;

                var page = report.Document.Pages[pageNumber - 1];

                var destinationPageNumber = indicatorOutline.PageNumber + indicatorsPageOffset;
                var destinationVerticalPosition = indicatorOutline.VerticalPosition;

                page.Elements.Add(
                    new Link(pageElementPosition.HorizontalPosition + horizontalPositionOffset,
                        pageElementPosition.VerticalPosition + verticalPositionOffset,
                        pageElementPosition.Width, pageElementPosition.Height,
                        new XYDestination(destinationPageNumber, 0, destinationVerticalPosition)));
            }
        }

        private PdfReportSection GetCompanyOverviewSection(ReportDataSource ds)
        {
            var generator = new CompanyOverviewSection();

            return generator.GeneratePdfReportSection(ds);
        }

        private PdfReportSection GetIssuesSection(ReportDataSource ds)
        {
            var generator = new IssuesSection();

            return generator.GeneratePdfReportSection(ds);
        }

        private PdfReportSection GetIndicatorsSection(ReportDataSource ds)
        {
            var generator = new IndicatorSection();

            return generator.GeneratePdfReportSection(ds);
        }

        private PdfReportSection GetSourcesSection(ReportDataSource ds)
        {
            var generator = new SourcesSection();

            return generator.GeneratePdfReportSection(ds);
        }

        private PdfReportSection GetFinalSection(ReportDataSource ds)
        {
            var generator = new FinalSection();

            return generator.GeneratePdfReportSection(ds);
        }

        private void AddPageFooter(PdfReport report)
        {
            var pageNumber = 1;
            var pageCount = report.Document.Pages.Count;
            
            float top;
            const float imageTopOffset = 9f;
            const float labelTopOffset = 5f;
            
            const float lineLeftOffset = 80f;
            const float lineWidth = 415f;
            const float imageLeftOffset = 75f;
            const float labelLeftOffset = 480f;


            for (var i = 0; i < report.Document.Pages.Count; i++)
            {
                var page = report.Document.Pages[i];

                top = 790f;
                var topMagin = page.Dimensions.TopMargin;
                if (topMagin.NearlyEq(0))
                {
                    top = 810f;
                }

                var leftMagin = page.Dimensions.LeftMargin;

                if (leftMagin.NearlyEq(0))
                {
                    leftMagin = 40f;
                }

                page.Elements.Add(new Line(leftMagin + lineLeftOffset, top, leftMagin + lineLeftOffset + lineWidth, top, 0.5f, RgbColor.Gray));
                
                page.Elements.Add(new Image(ImageData.GetImage(CompanyLogoFooterImage),
                    leftMagin + imageLeftOffset, top - imageTopOffset, 0.24f) { Align = Align.Right, VAlign = VAlign.Top });

                page.Elements.Add(new Label(string.Format("{0} | {1}", pageNumber, pageCount),
                    leftMagin + labelLeftOffset, top - labelTopOffset, 50, 10, Fonts.CalibriRegular, 10, TextAlign.Right, RgbColor.DimGray));

                pageNumber++;
            }
        }
    }
}
